package com.woniu.book.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.enums.ExceptionCode;
import com.ruoyi.common.exception.CustomException;
import com.woniu.apartment.domain.Bed;
import com.woniu.book.domain.Booking;
import com.woniu.book.domain.MsgReceive;
import com.woniu.book.domain.MsgSend;
import com.woniu.book.dto.BookingDto;
import com.woniu.book.feign.ApartmentFeign;
import com.woniu.book.feign.CouponFeign;

import com.woniu.book.feign.FinanceFeign;
import com.woniu.book.feign.UserFeign;
import com.woniu.book.mapper.BookingMapper;
import com.woniu.book.mapper.MsgReceiveMapper;
import com.woniu.book.mapper.MsgSendMapper;
import com.woniu.book.service.BookService;
import com.woniu.book.vo.BookingListVo;
import com.woniu.book.vo.BookingVo;
import com.woniu.coupon.domain.Coupon;
import com.woniu.finance.domain.ApartDepositReceivable;
import com.woniu.finance.domain.ApartFee;
import com.woniu.user.domain.Customer;
import com.woniu.user.domain.CustomerCoupon;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.awt.print.Book;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

@Service
public class BookServiceImpl implements BookService, BeanFactoryAware {

    @Autowired
    private BookService bookService;

    @Autowired
    private BookingMapper bookMapper;

    @Autowired
    private ApartmentFeign apartmentFeign;

    @Autowired
    private CouponFeign couponFeign;

    @Autowired
    private UserFeign userFeign;

    @Autowired
    private FinanceFeign financeFeign;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MsgSendMapper msgSendMapper;

    @Autowired
    private MsgReceiveMapper msgReceiveMapper;


    @Override
//    @GlobalTransactional,
    @Transactional
    public void makeBook(BookingDto booking) {

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String userId = request.getHeader("userId");
        Integer customerId = Integer.parseInt(userId);

        booking.setCustomerId(customerId);

        Integer payType = booking.getPayType();
        BigDecimal rent = booking.getRent();
        Integer rentDuration = booking.getRentDuration();

        System.out.println(beanFactory);

        BigDecimal prePay = bookService.totalPay(payType, rent, rentDuration);
        BigDecimal afterPay = bookService.CalculateDiscount(booking, prePay);

        // 用this会导致事务失效
//        BigDecimal prePay = this.totalPay(payType,rent,rentDuration);
//        BigDecimal afterPay = this.CalculateDiscount(booking,prePay);


        //修改床位为已占有未付款
        Integer bedId = booking.getBedId();
        Bed bed = new Bed();
        bed.setId(Long.valueOf(bedId));
        bed.setStatus(3L);
        apartmentFeign.edit(bed);


        //生成订单
        Booking newBooking = new Booking();
        BeanUtils.copyProperties(booking, newBooking);
        //设置订单的其他属性
        newBooking.setBookStatus(1); //未付款
        newBooking.setStartDate(booking.getCheckInDate());//开始入住时间
        newBooking.setDuration(booking.getRentDuration());//预定周数
        newBooking.setShowFlag(1);
        newBooking.setBookingCreatetime(new Date());
        newBooking.setPayType(payType);

        newBooking.setTotalPay(prePay);//折扣之前的金额
        newBooking.setDiscountAmount(prePay.subtract(afterPay));//减免金额
        newBooking.setRealPay(afterPay);//实际支付金额


        bookMapper.insert(newBooking);

        //生成订单的同时在消息发送表添加记录
//        MsgSend msgSend = new MsgSend();
//        msgSend.setMsg(booking.getId() + "");
//        msgSend.setStatus(0);
//        msgSend.setMsgType(1);
//        msgSendMapper.insert(msgSend);


        //财务系统
        //添加押金记录
        ApartDepositReceivable apartDepositReceivable = new ApartDepositReceivable();
        apartDepositReceivable.setBookingId(booking.getId());
        apartDepositReceivable.setDeposit(booking.getDeposit());
        apartDepositReceivable.setDepositTime(new Date());
        apartDepositReceivable.setDepositReturnBack(0);

        financeFeign.feignAddApartDepositReceivable(apartDepositReceivable);
        //添加公寓缴费记录
        Integer payType2 = booking.getPayType();

        List<ApartFee> apartFeeList = new ArrayList<>();

        if (payType2 == 0) {
            //一次性付款
            ApartFee apartFee = new ApartFee();
            apartFee.setBookId(booking.getId());
            apartFee.setFeeAmount(afterPay);
            apartFee.setPaySeq(0);
            apartFee.setPaid(0);
            apartFeeList.add(apartFee);
        } else if (payType2 == 1) {
            //分期付款

            Date incrementTime = booking.getCheckInDate();
            for (int i = 0; i < rentDuration; i++) {

                ApartFee apartFee = new ApartFee();

                Calendar instance = Calendar.getInstance();
                instance.setTime(incrementTime);
                instance.add(Calendar.WEEK_OF_YEAR, 1);
                incrementTime = instance.getTime();


                apartFee.setPlanPayTime(incrementTime);
                apartFee.setBookId(booking.getId());
                apartFee.setFeeAmount(booking.getRent());
                apartFee.setPaySeq(i + 1);
                apartFee.setPaid(0);
                apartFeeList.add(apartFee);
            }

        }

        //insert公寓缴费记录
        financeFeign.feignBatchAddApartFee(apartFeeList);


        //放入延时队列，48小时不付款，自动取消订单放入延时队列，48小时不付款，自动取消订单
        rabbitTemplate.convertAndSend(UserConstants.DELAY_ORDER_EXCHANGE, UserConstants.DELAY_ORDER_ROUTING_KEY, booking.getId());

    }

    //计算使用优惠卷后的金额
    public BigDecimal CalculateDiscount(BookingDto booking, BigDecimal prePay) {
        Integer couponId = booking.getCouponId();
        Integer customerId = booking.getCustomerId();

        //是否使用了优惠卷
        if (booking.getCouponId() == null) {
            //没有使用优惠卷
            return prePay;
        }

        //使用了优惠卷
        //查出优惠卷
        Coupon coupon = couponFeign.getCouponById(Long.valueOf(couponId));
        //判断是否满足优惠卷使用门槛
        if (coupon.getCouponCategory() == 3) {
            BigDecimal totalAmount = coupon.getTotalAmount();
            if ((prePay.compareTo(totalAmount)) < 0) {
                throw new CustomException(ExceptionCode.NOT_MEETING_THE_COUPON_USAGE_THRESHOLD);
            }
        }
        //若使用了优惠券把优惠券修改为已经使用
        //查是否使用的是用户个人
        CustomerCoupon customerCoupon = userFeign.getByCustomerIdAndCouponId(customerId, couponId);

//        if (customerCoupon != null) {
//            userFeign.editCustomerCouponStatus(customerId, couponId);
//            //是否是新人优惠券?
//            if (coupon.getCouponCategory() == 1) {
//                //使用了新人优惠券
//                Customer customer = new Customer();
//                customer.setCustomerId(Long.valueOf(customerId));
//                customer.setNewCustomerCouponUsed(2L);
//                userFeign.editCustomer(customer);
//            }
//        }
        //optional
        Optional.ofNullable(customerCoupon).ifPresent(item -> {
            //TODO 是否过期???(不想写了)
            userFeign.editCustomerCouponStatus(customerId, couponId);
            //是否是新人优惠券?
            if (coupon.getCouponCategory() == 1) {
                //使用了新人优惠券
                Customer customer = new Customer();
                customer.setCustomerId(Long.valueOf(customerId));
                customer.setNewCustomerCouponUsed(2L);
                userFeign.editCustomer(customer);
            }
        });

        BigDecimal discountRate = coupon.getDiscountRate();
        BigDecimal deductionAmount = coupon.getDeductionAmount();
        //计算折扣价格
        BigDecimal afterTotal = null;
        if (discountRate != null) {
            //此优惠券有折扣
            afterTotal = prePay.multiply(discountRate);
        }
        if (deductionAmount != null) {
            //此优惠券有满减
            afterTotal = prePay.subtract(deductionAmount);
        }


        return afterTotal;
    }

    @Override
    public Integer modifyBookingStatus(Long bookingId) {
        return bookMapper.updateBookingStatus(bookingId);
    }

    @Override
    public Booking queryBookByIdForFinance(Long bookId) {
        return bookMapper.selectBookByIdForFinance(bookId);
    }

    //计算使用优惠券前的价格
    public BigDecimal totalPay(Integer payType, BigDecimal rent, Integer rentDuration) {
        if (payType == 1) { //周期付款
            return rent;
        } else {  //一次性付清
            return rent.multiply(BigDecimal.valueOf(rentDuration));
        }
    }


    @Override
    public BookingVo queryBookById(Long bookingId) {
        return bookMapper.selectBookById(bookingId);
    }

    @Override
    public List<Booking> queryBookByIds(List<Long> ids) {
        return bookMapper.selectBookByIds(ids);
    }



//    @RabbitListener(queues = UserConstants.DLX_ORDER_QUEUE)
    public void cancelBook(Message message, Channel channel) throws IOException {
        System.out.println("收到消息");
        //TODO 为什么限流
        channel.basicQos(1);

        //消息反序列化
        String msg = new String(message.getBody());
        ObjectMapper objectMapper = new ObjectMapper();
        MsgSend msgSend = objectMapper.readValue(msg, MsgSend.class);

        //查询数据库是否已经有这条消息了
        MsgReceive msgReceive = msgReceiveMapper.selectByPrimaryKey(msgSend.getId());
        if (msgReceive == null) {
            MsgReceive newMsgReceive = new MsgReceive();
            BeanUtils.copyProperties(msgSend, newMsgReceive);
            msgReceiveMapper.insert(newMsgReceive);


            Long bookId = Long.valueOf(msgSend.getMsg());

            //检查订单是否已经付款
            int status = bookMapper.isPay(bookId);
            if (status != 2) {
                bookMapper.cancelBook(bookId);
            }

            //通知生产者消息已经处理完毕
            msgSend.setStatus(1);
            msgSendMapper.updateByPrimaryKey(msgSend);

            //更改接收消息表的状态
            newMsgReceive.setStatus(1);
            msgReceiveMapper.updateByPrimaryKey(newMsgReceive);
        } else {
            //不消费
            return;
        }
    }

    @RabbitListener(queues = UserConstants.DLX_ORDER_QUEUE)
    public void cancelBook(Long bookId) throws IOException {
        System.out.println("收到消息:"+bookId);
            //检查订单是否已经付款
            int status = bookMapper.isPay(bookId);
            if (status != 2) {
                bookMapper.cancelBook(bookId);
            }

    }

    //定时任务专门发消息
//    @Scheduled(fixedRate = 5000)
    public void sendBookingMsg() throws JsonProcessingException {
        MsgSend msgSend = new MsgSend();
        msgSend.setStatus(0);

        List<MsgSend> UnsentMessages = msgSendMapper.select(msgSend);

        ObjectMapper objectMapper = new ObjectMapper();

        for (MsgSend unsentMessage : UnsentMessages) {
            String s = objectMapper.writeValueAsString(unsentMessage);
            rabbitTemplate.convertAndSend(UserConstants.DELAY_ORDER_EXCHANGE, UserConstants.DELAY_ORDER_ROUTING_KEY, s);
        }
        System.out.println("定时任务发送了消息");
    }






    BeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public List<BookingListVo> getBooksById(Long id) {
        return bookMapper.getBooksById(id);
    }

    @Override
    public void remove(Long id) {
        bookMapper.remove(id);
    }
}
